import {BaseService} from 'egg-cool-service';

const Duplex = require ('stream').Duplex;
// const md5 = require("md5");
// const fs = require("fs");
// const path = require("path");
const crypto = require ('crypto');
// const bluebird = require('bluebird')
// const PassThrough = require('stream').PassThrough;

export default class WxService extends BaseService {
    async fetchAccessToken () {
        const url = this.app.config.wxApi.accessTokenUrl.replace ('APPID' , this.app.config.wxApp.appId).replace ('APPSECRET' , this.app.config.wxApp.appSecret);
        console.log ('url' , url);
        const res = await this.ctx.curl (url , {dataType : 'json'});
        this.ctx.logger.info (`${ this.app.config.wxApp.name }#${ this.app.config.wxApp.accessTokenRedisKey } ==> ` + JSON.stringify (res.data));
        if ( res.status == 200 && res.data.access_token ) {
            console.log ('请求到了access_token' , res.data);
            await this.app.redisSet (this.app.config.wxApp.accessTokenRedisKey , res.data.access_token , res.data.expires_in - 300);
            return res.data.access_token;
        }
        return '';
    }

    async getAccessToken () {
        // await this.app.redisDel(this.app.config.wxApp.accessTokenRedisKey)
        let accessToken = await this.app.redisGet (this.app.config.wxApp.accessTokenRedisKey);
        // console.log('accessToken',accessToken)
        if ( !accessToken ) {
            accessToken = await this.fetchAccessToken ();
        }
        return accessToken;
    }

    getUploadDirName () {
        const date = new Date ();
        let month : number|string = Number.parseInt (String (date.getMonth ())) + 1;
        month = month.toString ().length > 1 ? month : `0${ month }`;
        const dir = `${ date.getFullYear () }${ month }${ date.getDate () }`;
        return dir;
    }

    bufferToStream (buffer) {
        let stream = new Duplex ();
        stream.push (buffer);
        stream.push (null);
        return stream;
    }

    // isBuffer(str){
    //     return str && typeof str === "object" && Buffer.isBuffer(str)
    // }

    sha1 (message) {
        return crypto.createHash ('sha1').update (message , 'utf8').digest ('hex');
    }

    async createQRCode (params) {
        // console.log('params',this.ctx.req.url)
        //生成小程序二维码
        const accessToken = await this.getAccessToken ();
        const url = this.app.config.wxApi.createQRCodeUrl.replace ('ACCESS_TOKEN' , accessToken);

        const filename =
                this.sha1 (`${ params.path }`) + '.png';
        // console.log(md5(1)==md5(1))
        // 最终要保存到的文件夹目录
        // const dirName = this.getUploadDirName();
        // const dir = path.resolve(__dirname, `../../public/wx_uploads/${dirName}`);
        // if (!fs.existsSync(dir)) {
        //     fs.mkdirSync(dir);
        // }
        // const localFilePath = path.resolve(__dirname, `../../public/wx_uploads/${dirName}`, filename);
        // const staticFilePath = `/wx_uploads/${dirName}/${filename}`
        // try {
        // 检测该名字的小程序码图片文件是否已存在
        // await bluebird.promisify(fs.access)(localFilePath, fs.constants.R_OK);
        // return staticFilePath
        // }catch (e) {
        try {
            // 小程序码不存在，则创建一张新的
            const res = await this.ctx.curl (url , {
                        // dataType: 'json',
                        method : "POST" ,
                        type : "POST" ,
                        // headers:{//自定义header
                        //     // "accept": "*/*",
                        //     // "content-type":"application/json;charset=utf-8"
                        //     "content-type":"application/json"
                        // },
                        contentType : 'json' ,
                        data : {
                            'path' : `${ params.path }` ,
                            'width' : this.app.config.wxApp.qrCodeWidth ,
                        }
                    }
            );
            // console.log(res)
            if ( res.status == 200 && res.data ) {
                // const base64Img = res.data.toString('base64')
                // return base64Img
                //614错误 行不通
                // console.log('res.data',res.data)
                // res.data.filename = '二维码'
                const result = await this.ctx.service.comm.qiniu.upload ({
                    initStream : this.bufferToStream (res.data) ,
                    filename ,
                    initDirName : "/二维码"
                });
                console.log ('二维码result' , result);
                return result.url;

                //图片存本地
                // const imgnameback = 'applet/'+filename + '.jpg'
                // const base64Img = res.data.toString('base64'); // base64图片编码字符串
                // const dataBuffer = new Buffer(base64Img, 'base64');
                // 保存到本地
                // fs.writeFile(localFilePath,dataBuffer,err=>{
                //     if(err){
                //         throw new Error('保存小程序二维码失败'+err);
                //     }else {
                //         console.log('保存成功',localFilePath)
                //         return localFilePath
                //     }
                // })
                // try {
                //     fs.writeFileSync(localFilePath,dataBuffer)
                //     console.log('保存成功',localFilePath)
                //     return staticFilePath
                // }catch(err){
                //     throw new Error('保存小程序二维码失败'+err);
                // }
            }
        } catch (e) {
            throw new Error ('获取小程序二维码信息失败' + e);
        }
        // }
    }

    // const tokenUrl = this.app.config.wx.tokenUrl;
    // const appid = this.app.config.wx.appid;
    // const appsecret = this.app.config.wx.appsecret;

    /********
     *
     * 登录逻辑实现
     *
     * */

    //获取sessionKey
    async getSessionKey (code) {
        let appid = this.app.config.wxApp.appId;
        let secret = this.app.config.wxApp.appSecret;
        let grant_type = 'authorization_code';
        let wecode = code;
        // 登录接口地址
        let url = `https://api.weixin.qq.com/sns/jscode2session?appid=${ appid }&secret=${ secret }&js_code=${ wecode }&grant_type=${ grant_type }`;
        // 请求登录接口
        const result = await this.app.curl (url , {
            dataType : 'json'
        });
        // console.log('微信登录',result)
        if ( result.data.openid ) {
            return {
                session_key : result.data.session_key ,
                openid : result.data.openid
            };
        } else {
            throw new Error ('获取openId失败');
        }
    }

    //解密加密的数据encryptedData
    decodeUserInfo (session_key , iv , encryptedData) {
        encryptedData = new Buffer (encryptedData , 'base64');
        session_key = new Buffer (session_key , 'base64');
        iv = new Buffer (iv , 'base64');
        const decipher = crypto.createDecipheriv ('aes-128-cbc' , session_key , iv);
        let decoded = decipher.update (encryptedData , 'base64' , 'utf8');
        decoded += decipher.final ('utf8');
        decoded = JSON.parse (decoded);
        return decoded;
    }

    /**
     *
     * 获取手机号
     * */
    async decodePhone (params) {
        let {iv , encryptedData} = params;
        let {ctx} = this;
        if ( this.ctx.wx_decoded && this.ctx.wx_decoded.session_key ) {
            let phone = this.decodeUserInfo (this.ctx.wx_decoded.session_key , iv , encryptedData);
            const {phoneNumber} = phone;
            let info = await ctx.repo.wx.Wx_user.findOne ({openid : this.ctx.wx_decoded.openid});
            if ( info ) {
                const newInfo = {... info , phoneNumber};
                await ctx.repo.wx.Wx_user.update (info.id , newInfo);
                return await this.userInfo ();
            } else {
                throw new Error ('数据库无此用户，请联系管理员');
            }
        } else {
            throw new Error ('暂未登录');
        }
    }

    async userInfo () {
        let {ctx} = this;
        if ( this.ctx.wx_decoded && this.ctx.wx_decoded.session_key ) {
            let info : any = await ctx.repo.wx.Wx_user.findOne ({openid : this.ctx.wx_decoded.openid});
            if ( info ) {
                info.role = (ctx.app.config.wxAuthMobiles.includes (info.phoneNumber)) ? 1 : 0;
                return info;
            } else {
                throw new Error ('数据库无此用户，请联系管理员');
            }
        } else {
            throw new Error ('暂未登录');
        }
    }

    /**
     * 生成token
     * @param user 用户对象
     * @param roleIds 角色集合
     * @param expires
     */
    async generateToken (code , openid , session_key , id) {
        const tokenInfo = {
            code ,
            openid ,
            session_key ,
            id
        };
        return this.app.jwt.sign (tokenInfo ,
                this.app.config.jwt.secret);
    }

    async login (params) {
        let {ctx} = this;
        let {code , nickName , avatarUrl , language , country , province , city} = params;
        let {session_key , openid} = await this.getSessionKey (code);
        const info = await ctx.repo.wx.Wx_user.findOne ({openid});
        const data = {
            nickName , avatarUrl , language , country , province , city , openid
        };
        console.log ('登录信息' , info);
        if ( !info ) {
            await ctx.repo.wx.Wx_user.save (data);
            let newInfo : any = await ctx.repo.wx.Wx_user.findOne ({openid});
            // 解密用户信息，可以将其存入数据库
            let token = await this.generateToken (code , openid , session_key , newInfo.id);
            await this.app.redisSet (`wx:user:${ newInfo.id }` , token);
            return token;
        } else {
            await ctx.repo.wx.Wx_user.update (info.id , data);
            // 解密用户信息，可以将其存入数据库
            let token = await this.generateToken (code , openid , session_key , info.id);
            await this.app.redisSet (`wx:user:${ info.id }` , token);
            return token;
        }
    }

    async logout () {
        if ( this.ctx.wx_decoded && this.ctx.wx_decoded.id ) {
            const {id} = this.ctx.wx_decoded;
            console.log ('退出登录' , id);
            await this.app.redisDel (`wx:user:${ id }`);
            return 0;
        } else {
            throw new Error ('暂未登录');
        }
    }

    /**
     * 用户列表
     * */
    async page (query) {
        const {keyword = ""} = query;
        const sql = `
        SELECT
            *
        FROM
            wx_user a
        WHERE 1 = 1
            ${ this.setSql (keyword ,
                'and ( a.nickName like ?) ' , [`%${ keyword }%`]) }
        `;
        const result = await this.sqlRenderPage (sql , query);
        return result;
    }

}
